
#include "cl_base_MultiClassClassification.h"

// [*] CMultiClassClassifyResult

void CMultiClassClassifyResult::myInit(int ClassAmount)
{
	this->ClassAmount = ClassAmount;

	this->matPrediction.Resize(ClassAmount * ClassAmount);
	this->PerClassAmountList_Real.Resize(ClassAmount);
	this->PerClassAmountList_Pred.Resize(ClassAmount);
	this->matConfusion.Resize(ClassAmount * ClassAmount);
	this->PrecisionList.Resize(ClassAmount);
	this->RecallList.Resize(ClassAmount);

	Clear();
}

void CMultiClassClassifyResult::Clear()
{
	int i;

	TotalCaseAmount = 0;
	matPrediction.Fill(0);
	PerClassAmountList_Real.Fill(0);
	PerClassAmountList_Pred.Fill(0);

	TruePosAmount = 0;

	TotalAccuracy = 1.0;
	matConfusion.Fill(0.0);
	for ( i = 0; i < ClassAmount; ++i )
		matConfusion.pElement[i + i * ClassAmount] = 1.0;
	PrecisionList.Fill(1.0);
	RecallList.Fill(1.0);
}

void CMultiClassClassifyResult::NewTest(int RealClassIndex, int Prediction)
{
	++TotalCaseAmount;
	++matPrediction.pElement[Prediction + RealClassIndex * ClassAmount];
	++PerClassAmountList_Real.pElement[RealClassIndex];
	++PerClassAmountList_Pred.pElement[Prediction];
	if (RealClassIndex == Prediction)
		++TruePosAmount;
}

void CMultiClassClassifyResult::Analyse()
{
	int i,j;
	double t;

	TotalAccuracy = (TotalCaseAmount<1 ? 1.0 : 1.0*TruePosAmount/TotalCaseAmount);
	for ( i = 0; i < ClassAmount; ++i )
		if (PerClassAmountList_Real.pElement[i] < 1)
		{
			for ( j = 0; j < ClassAmount; ++j )
				matConfusion.pElement[j + i * ClassAmount] = (j==i ? 1.0 : 0.0);
			RecallList.pElement[i] = 1.0;
		}
		else
		{
			t = 1.0 / PerClassAmountList_Real.pElement[i];
			for ( j = 0; j < ClassAmount; ++j )
				matConfusion.pElement[j + i * ClassAmount] = t * matPrediction.pElement[j + i * ClassAmount];
			RecallList.pElement[i] = t * matPrediction.pElement[i + i * ClassAmount];
		}
	for ( j = 0; j < ClassAmount; ++j )
		if (PerClassAmountList_Pred.pElement[j] < 1)
			PrecisionList.pElement[j] = 1.0;
		else
		{
			t = 1.0 / PerClassAmountList_Pred.pElement[j];
			PrecisionList.pElement[j] = t * matPrediction.pElement[j + j * ClassAmount];
		}
}

void CMultiClassClassifyResult::OutputToStream(ostream& outStream)
{
	outStream.precision(3);
	outStream<<"{ Total Accuracy = "<<fixed<<TotalAccuracy*100.0<<" ("<<TruePosAmount<<" / "<<TotalCaseAmount<<")"<<endl;
	OutputConfusionMatrixToStream(outStream);
	OutputPrecisionRecallToStream(outStream);
	outStream<<"}"<<endl;
}

void CMultiClassClassifyResult::OutputConfusionMatrixToStream(ostream& outStream)
{
	int i,j;

	outStream.precision(2);
	outStream<<"\t";
	for ( j = 0; j < ClassAmount; ++j )
		outStream<<"\t"<<j;
	outStream<<endl;
	for ( i = 0; i < ClassAmount; ++i )
	{
		outStream<<"\t"<<i;
		for ( j = 0; j < ClassAmount; ++j )
			outStream<<"\t"<<fixed<<matConfusion.pElement[j + i * ClassAmount]*100.0;
		outStream<<endl;
	}
}

void CMultiClassClassifyResult::OutputPrecisionRecallToStream(ostream& outStream)
{
	int i;

	outStream.precision(3);
	for ( i = 0; i < ClassAmount; ++i )
	{
		outStream<<i<<"\t"<<i<<":";
		outStream<<"\tPrecision = "<<fixed<<PrecisionList.pElement[i]*100.0;
		outStream<<"\tRecall = "<<fixed<<RecallList.pElement[i]*100.0;
		outStream<<"\t(TruePos / Real / Pred = ";
		outStream<<matPrediction.pElement[i + i * ClassAmount]<<" / ";
		outStream<<PerClassAmountList_Real.pElement[i]<<" / ";
		outStream<<PerClassAmountList_Pred.pElement[i]<<")";
		outStream<<endl;
	}
}

